#include "BasicShader.h"

// Initialise static data members.
BasicShader* BasicShader::m_pEffect = 0;
bool BasicShader::m_instanceFlag = false;

//-------------------------------------------------------------------
BasicShader::BasicShader()
{ }

//-------------------------------------------------------------------
BasicShader::~BasicShader()
{ }

//-------------------------------------------------------------------
void BasicShader::Initialise(ID3D11Device* device)
{
	// Create the shader filename string.
	std::wstring filename = L"character-controller/fx/Basic.hlsl";

	// Compile the shader file using the ShaderUtil class.
	ShaderUtil::CompileEffect(device, filename, &m_Effect);

	// Link the C++ effect variables to the shader HLSL variables.
	BindVariables();

	// Create the vertex input layout for this shader.
	CreateInputLayout(device);
}

//-------------------------------------------------------------------
void BasicShader::Shutdown()
{
	ReleaseCOM(m_Effect);
	ReleaseCOM(m_InputLayout);

	SafeDelete(m_pEffect);
}

//-------------------------------------------------------------------
BasicShader* BasicShader::GetInstance()
{
	// If this class has not yet been instantiated.
	if (m_instanceFlag == false)
	{
		m_pEffect = new BasicShader;		// Instantiate the shader.
		m_instanceFlag = true;				// Set instance flag to true.
		return m_pEffect;					// Return the instance pointer.
	}
	// If this class has been instantiated.
	else
	{
		return m_pEffect;					// Return the instance pointer.
	}
}

//-------------------------------------------------------------------
void BasicShader::BindVariables()
{	
	//==============================
	// Bind Shader Techniques
	//==============================

	Light1Tech					= m_Effect->GetTechniqueByName("Light1");
	Light2Tech					= m_Effect->GetTechniqueByName("Light2");
	Light3Tech					= m_Effect->GetTechniqueByName("Light3");

	Light0TexTech				= m_Effect->GetTechniqueByName("Light0Tex");
	Light1TexTech				= m_Effect->GetTechniqueByName("Light1Tex");
	Light2TexTech				= m_Effect->GetTechniqueByName("Light2Tex");
	Light3TexTech				= m_Effect->GetTechniqueByName("Light3Tex");

	Light0TexAlphaClipTech		= m_Effect->GetTechniqueByName("Light0TexAlphaClip");
	Light1TexAlphaClipTech		= m_Effect->GetTechniqueByName("Light1TexAlphaClip");
	Light2TexAlphaClipTech		= m_Effect->GetTechniqueByName("Light2TexAlphaClip");
	Light3TexAlphaClipTech		= m_Effect->GetTechniqueByName("Light3TexAlphaClip");

	Light1FogTech				= m_Effect->GetTechniqueByName("Light1Fog");
	Light2FogTech				= m_Effect->GetTechniqueByName("Light2Fog");
	Light3FogTech				= m_Effect->GetTechniqueByName("Light3Fog");

	Light0TexFogTech			= m_Effect->GetTechniqueByName("Light0TexFog");
	Light1TexFogTech			= m_Effect->GetTechniqueByName("Light1TexFog");
	Light2TexFogTech			= m_Effect->GetTechniqueByName("Light2TexFog");
	Light3TexFogTech			= m_Effect->GetTechniqueByName("Light3TexFog");

	Light0TexAlphaClipFogTech	= m_Effect->GetTechniqueByName("Light0TexAlphaClipFog");
	Light1TexAlphaClipFogTech	= m_Effect->GetTechniqueByName("Light1TexAlphaClipFog");
	Light2TexAlphaClipFogTech	= m_Effect->GetTechniqueByName("Light2TexAlphaClipFog");
	Light3TexAlphaClipFogTech	= m_Effect->GetTechniqueByName("Light3TexAlphaClipFog");

	//==============================
	// Bind Shader Variables
	//==============================

	WorldViewProj				= m_Effect->GetVariableByName("gWorldViewProj")->AsMatrix();
	World						= m_Effect->GetVariableByName("gWorld")->AsMatrix();
	WorldInvTranspose			= m_Effect->GetVariableByName("gWorldInvTranspose")->AsMatrix();
	TexTransform				= m_Effect->GetVariableByName("gTexTransform")->AsMatrix();
	EyePosW						= m_Effect->GetVariableByName("gEyePosW")->AsVector();
	FogColor					= m_Effect->GetVariableByName("gFogColor")->AsVector();
	FogStart					= m_Effect->GetVariableByName("gFogStart")->AsScalar();
	FogRange					= m_Effect->GetVariableByName("gFogRange")->AsScalar();
	DirLights					= m_Effect->GetVariableByName("gDirLights");
	Mat							= m_Effect->GetVariableByName("gMaterial");
	DiffuseMap					= m_Effect->GetVariableByName("gDiffuseMap")->AsShaderResource();
}

//-------------------------------------------------------------------
void BasicShader::CreateInputLayout(ID3D11Device* device)
{
	HRESULT hr;

	// Create input element description that corresponds to the vertex shader signature.
	const D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[3] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}
	};

	// Get signature of vertex shader.
	D3DX11_PASS_DESC passDesc;
	Light3Tech->GetPassByIndex(0)->GetDesc(&passDesc);

	// Create the input layout.
	hr = device->CreateInputLayout(inputLayoutDesc, 3, passDesc.pIAInputSignature,
		passDesc.IAInputSignatureSize, &m_InputLayout);

	// Output error message if the input layout creation failed.
	if (FAILED(hr))
	{
		MessageBox(NULL, L"BasicShader::InputLayout Error!", L"Error", MB_OK);
	}
}

//-------------------------------------------------------------------
ID3D11InputLayout* BasicShader::GetInputLayout() const
{
	return m_InputLayout;
}

//-------------------------------------------------------------------
void BasicShader::SetWorldViewProj(CXMMATRIX M)
{
	WorldViewProj->SetMatrix(reinterpret_cast<const float*>(&M));
}

//-------------------------------------------------------------------
void BasicShader::SetWorld(CXMMATRIX M)
{
	World->SetMatrix(reinterpret_cast<const float*>(&M));
}

//-------------------------------------------------------------------
void BasicShader::SetWorldInvTranspose(CXMMATRIX M)
{
	WorldInvTranspose->SetMatrix(reinterpret_cast<const float*>(&M));
}

//-------------------------------------------------------------------
void BasicShader::SetTexTransform(CXMMATRIX M)
{
	TexTransform->SetMatrix(reinterpret_cast<const float*>(&M));
}

//-------------------------------------------------------------------
void BasicShader::SetEyePosW(const XMFLOAT3& v)
{
	EyePosW->SetRawValue(&v, 0, sizeof(XMFLOAT3));
}

//-------------------------------------------------------------------
void BasicShader::SetFogColor(const XMVECTOR v)
{
	FogColor->SetFloatVector(reinterpret_cast<const float*>(&v));
}

//-------------------------------------------------------------------
void BasicShader::SetFogStart(float f)
{
	FogStart->SetFloat(f);
}

//-------------------------------------------------------------------
void BasicShader::SetFogRange(float f)
{
	FogRange->SetFloat(f);
}

//-------------------------------------------------------------------
void BasicShader::SetDirLights(const DirectionalLight* lights)
{
	DirLights->SetRawValue(lights, 0, 3*sizeof(DirectionalLight));
}

//-------------------------------------------------------------------
void BasicShader::SetMaterial(const Material& mat)
{
	Mat->SetRawValue(&mat, 0, sizeof(Material));
}

//-------------------------------------------------------------------
void BasicShader::SetDiffuseMap(ID3D11ShaderResourceView* tex)
{
	DiffuseMap->SetResource(tex);
}